/*
 * 连接接收端处理逻辑
 */

#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/stat.h> 
#include <fcntl.h>
#include <unistd.h>
#include <sys/time.h>
#include <arpa/inet.h>

#include <sm2.h>
#include <sm3.h>
#include <sm4.h>

#include "common.h"
#include "key_management.h"
#include "tools.h"

#define BUF_SIZE 1024
#define PREFIX (sizeof(uint32_t)*2+sizeof(uint8_t))
#define MIN(x, y) (x < y ? x : y)

uint8_t send_buf[BUF_SIZE];     // 发送缓冲区

/*
 * 获取本地蓝牙设备地址
 *
 * return 返回的是一个假地址
 */ 
uint8_t* getBluetoothAddress() {
    uint8_t *bd_addr = (uint8_t*)malloc(18);
    strcpy(bd_addr, "00:00:00:00:00:00");
    return bd_addr;
}

/*
 * 生成伪PIN码
 */
void generatePINUnsafe() {
    uint8_t *bd_addr = (uint8_t*)malloc(18);
    strcpy(bd_addr, "ff:ff:ff:ff:ff:ff");

    PIN *pin = (PIN*)malloc(sizeof(PIN));
    pin->bd_addr = bd_addr;
    pin->pin = generate_secure_random_number(KEY_LENGTH);
    pin->flag = 0x00;
    pin->uuid = getUid(pin->pin);
    pin->start_time = 0;
    pin->end_time = 0;
    pin->expiration_time = 0;
    setPIN(pin);

    printHex(pin->pin, KEY_LENGTH); // 十六进制的方式打印伪PIN码
}

/**
 * 连接接收方的初次连接处理函数（SM2加解密）
 */
void firstConn(int fd, uint8_t *buf, size_t len, uint32_t uuid) {
    printf("接收到初次连接请求...\n");

    int ret = SUCCESS, enc_len = 0;
    uint8_t *ret_data = NULL, *enc_data = NULL;
    uint8_t msg[128];
    uint8_t iv[16] = {0};
    PIN *pin = getPIN(uuid);                 // 通过uid获取对应的PIN对象
    uint8_t *token = getToken(pin->pin);    // 通过伪PIN码获取token

    do {
        if (pin == NULL || !pinIsValid(pin)) {
            // 伪PIN码不存在或伪PIN码不在有效时间内，返回
            ret = FAILURE;
            strcpy(msg + sizeof(int), "uuid invalid");
            break;
        }
        printf("token: ");
        printHex(token, KEY_LENGTH);

        uint8_t *addr = pin->bd_addr;
        uint8_t addr_[18];
        enc_data = (uint8_t*)malloc(len + 16);
        if (GM_SM4_decrypt(enc_data, &enc_len, buf, len, token, iv) != SUCCESS) {   // SM4解密接收到的数据
            // 调用函数解密时出错，返回
            ret = FAILURE;
            strcpy(msg + sizeof(int), "uuid invalid");
            break;
        }

        uint32_t tmp, data_size;
        uint8_t randomA[RANDOM_LENGTH], *randomB, randomC[RANDOM_LENGTH];
        memcpy(&tmp, enc_data, sizeof(uint32_t));                                   // 从解密后数据中拿到发送者携带的uuid
        memcpy(addr_, enc_data + sizeof(uint32_t), 17);                             // 从解密后数据中拿到发送者携带的蓝牙设备地址
        addr_[17] = 0;
        if (uuid != tmp || strcmp(addr, addr_)) {                                   // 比较携带的uuid和蓝牙设备地址是否有效
            // uid、tmp不等则解密失败，返回
            // 或设备地址不匹配则返回
            ret = FAILURE;
            strcpy(msg + sizeof(int), "uuid invalid");
            break;
        }
        // 成功解密且设备地址、uid校验成功，流程继续，从解密后的数据中获取第一个随机数
        memcpy(randomA, enc_data + sizeof(uint32_t) + 17, RANDOM_LENGTH);

        EC_KEY *eckey_mobile = NULL;
        EC_KEY *eckey_car = GM_SM2_key_new();   // 生成SM2密钥对

        uint8_t *pub_key = NULL;
        randomB = generate_secure_random_number(RANDOM_LENGTH); // 生成密码学安全的随机数
        GM_SM2_key_encode((char**)&pub_key, eckey_car, 0);  // 从密钥对中编码得到公钥的明文
        int pub_key_len = strlen(pub_key);
        uint8_t *bd_addr = getBluetoothAddress();               // 获取本机的虚假蓝牙设备地址，因为是模拟，所以拿的是虚假的就够了
        printf("pub_key(car): %s\n", pub_key);

        // 加密返回uid、随机数B、设备地址、公钥
        // 返回数据的格式： data_size | SM4(uuid, ret, randomB, bd_addr, pub_key)
        size_t ret_data_len = sizeof(uint32_t)*2 + RANDOM_LENGTH + 17 + pub_key_len;
        ret_data = (uint8_t*)malloc(ret_data_len);
        // 组装要加密的数据
        memcpy(ret_data, &uuid, sizeof(uint32_t));              // 将uuid存到加密缓冲区
        memcpy(ret_data + sizeof(uint32_t), &ret, sizeof(uint32_t));    // 将响应码存到加密缓冲区
        memcpy(ret_data + sizeof(uint32_t)*2, randomB, RANDOM_LENGTH);  // randomB存到加密缓冲区
        memcpy(ret_data + sizeof(uint32_t)*2 + RANDOM_LENGTH, bd_addr, 17); // 本机设备地址存到加密缓冲区
        memcpy(ret_data + sizeof(uint32_t)*2 + RANDOM_LENGTH + 17, pub_key, pub_key_len);   // 自身公钥存到加密缓冲区
        if (sizeof(enc_data) < ret_data_len + 16) {
            // enc_data的空间不足以复用，需要释放空间并重新分配足够的空间
            free(enc_data);
            enc_data = (uint8_t*)malloc(ret_data_len + 16);
        }
        // 使用SM4加密数据，加密后的数据保存在enc_data偏移量为4B的位置
        GM_SM4_encrypt(enc_data + sizeof(uint32_t), &enc_len, ret_data, ret_data_len, token, iv);
        // 将data_size(enc_len)保存到enc_data一并返回
        memcpy(enc_data, &enc_len, sizeof(uint32_t));   // 响应的数据长度保存到发送缓冲区（即enc_data，加密后的数据已经在enc_data中了）
        send(fd, enc_data, enc_len + sizeof(uint32_t), 0);  // 发送数据
        free(bd_addr);

        // 等待数据返回(随机数C、公钥)
        len = recv(fd, buf, BUF_SIZE, 0);                   // 等待响应并接收数据
        memcpy(&data_size, buf, sizeof(uint32_t));          // 从响应中拿到本轮要接收的数据长度
        if (sizeof(enc_data) < data_size + 16) {
            // enc_data的空间不足以复用，需要释放空间并重新分配足够的空间
            free(enc_data);
            enc_data = (uint8_t*)malloc(data_size + 16);
        }
        if (GM_SM4_decrypt(enc_data, &enc_len, buf + sizeof(uint32_t), data_size, token, iv) != SUCCESS) {  // 解密
            // 解密出错，返回
            ret = FAILURE;
            strcpy(msg + sizeof(int), "uuid invalid");
            break;
        }
        memcpy(&tmp, enc_data, sizeof(uint32_t));   // 从解密后的明文中，拿到移动端发送的uuid
        if (uuid != tmp) {                          // 从解密后拿到的uuid和自身保存的uuid不一致可知，解密可能是失败的
            // 解密失败，返回
            ret = FAILURE;
            strcpy(msg + sizeof(int), "uuid invalid");
            break;
        }
        // 解密成功，获取第三个随机数及移动终端的公钥
        memcpy(randomC, enc_data + sizeof(uint32_t), RANDOM_LENGTH);                // 从解密后的明文拿到randomC
        memcpy(pub_key, enc_data + sizeof(uint32_t) + RANDOM_LENGTH, pub_key_len);  // 从解密后的明文拿到移动端的公钥
        pub_key[pub_key_len] = 0;
        printf("pub_key(mobile): %s\n", pub_key);
        printf("randomA : ");
        printHex(randomA, RANDOM_LENGTH);
        printf("randomB : ");
        printHex(randomB, RANDOM_LENGTH);
        printf("randomC : ");
        printHex(randomC, RANDOM_LENGTH);

        eckey_mobile = EC_KEY_new();
        GM_SM2_key_decode(&eckey_mobile, pub_key, 0);   // 将公钥明文编码保存到eckey_mobile指针

        free(pub_key);
        pub_key = NULL;

        // 生成对称加密密钥，并保存连接相关信息
        KEY *key = (KEY*)malloc(sizeof(KEY));
        key->uuid = uuid;
        key->flag = isAdmin(uuid) ? 0x01 : 0x00;    // 标记权限
        key->eckey_car = eckey_car;
        key->eckey_mobile = eckey_mobile;
        key->expiration_time = pin->expiration_time;    // 保存移动端的过期时间
        key->recent_request = 0;
        key->secret = generateSecret(randomA, randomB, randomC);    // 生成对称密钥并保存
        free(randomB);
        setKEY(key);
        strcpy(msg + sizeof(int)*2, "connected");
        printf("初次连接成功！\n\n\n");
    } while(0);

    // 无论是连接成功返回的成功状态信息还是连接失败返回的错误信息，都在这里做统一的处理
    // 若连接成功，这里返回最后一轮数据；若连接失败，这里返回出错信息
    memcpy(msg, &uuid, sizeof(uint32_t));                   
    memcpy(msg + sizeof(uint32_t), &ret, sizeof(uint32_t)); 
    memcpy(&ret, msg + sizeof(uint32_t), sizeof(uint32_t)); 
    GM_SM4_encrypt(enc_data, &enc_len, msg, 8, token, iv);

    memcpy(send_buf, &enc_len, sizeof(uint32_t));
    memcpy(send_buf + sizeof(uint32_t), enc_data, enc_len);
    send(fd, send_buf, sizeof(uint32_t) + enc_len, 0);
    if (ret == SUCCESS) destroyPIN(uuid);    // 连接成功，销毁伪PIN码

    if (ret_data != NULL) {
        free(ret_data);
        ret_data = NULL;
    }

    if (enc_data != NULL) {
        free(enc_data);
        enc_data = NULL;
    }

}

/**
 * 连接接收方的二次连接处理函数（SM2加解密 + 数字签名）
 */
void secondConn(int fd, uint8_t *buf, size_t len, uint32_t uuid) {
    printf("接收到二次连接请求...\n");

    int ret = SUCCESS;
    uint32_t data_size;
    uint8_t msg[128];
    uint8_t *ret_data = NULL;
    uint8_t *enc_data = NULL;
    KEY *key = getKey(uuid);        // 根据uuid拿到KEY*，KEY保存了连接双方的密钥对
    EC_KEY *mobile_key = NULL;
    EC_KEY *car_key = NULL;

    do {
        if (key == NULL || !keyIsValid(key)) {
            // uid是初次连接或者uid已过期，直接返回
            ret = FAILURE;
            strcpy(msg + sizeof(int)*2, "Identity has expired");
            break;
        }
        // 获取连接双方的公钥
        mobile_key = key->eckey_mobile;     // 从KEY中取出移动端的密钥对（里面只有公钥）
        car_key = key->eckey_car;           // 从KEY中取出自身的密钥对

        uint8_t *dec_data = NULL;
        uint32_t dec_len;
        if (verifyAndDecrypt(&dec_data, &dec_len, buf, len, car_key, mobile_key, uuid) != SUCCESS) {    // SM2验签并解密
            ret = FAILURE;
            strcpy(msg + sizeof(int)*2, "Data decryption failed");
            break;
        }

        // 拿到随机数A
        uint32_t time;
        uint8_t randomA[RANDOM_LENGTH], *randomB, randomC[RANDOM_LENGTH];

        memcpy(&time, dec_data + sizeof(uint32_t), sizeof(uint32_t));   // 从解密后的明文中获取移动端发送的时间戳time
        if (!requestIsValid(key, time)) {       // 根据时间戳判断是否是重放数据
            // 验签和解密成功，但是时间戳已过期，可能是重放数据，直接返回
            ret = FAILURE;
            strcpy(msg + sizeof(int)*2, "request has expired");
            break;
        }
        memcpy(randomA, dec_data + sizeof(uint32_t)*2, RANDOM_LENGTH);  // 从解密后的明文获取randomA
        free(dec_data);
        dec_data = NULL;

        randomB = generate_secure_random_number(RANDOM_LENGTH);

        // 加密返回数据 ret(状态码) | randomB
        size_t ret_data_len = sizeof(uint32_t) * 2 + RANDOM_LENGTH;
        ret_data = (uint8_t*)malloc(sizeof(uint8_t) * ret_data_len);

        memcpy(ret_data, &uuid, sizeof(uint32_t));                      // 保存uuid到待加密缓冲区
        memcpy(ret_data + sizeof(uint32_t), &ret, sizeof(uint32_t));    // 保存响应码到待加密缓冲区
        memcpy(ret_data + sizeof(uint32_t)*2, randomB, RANDOM_LENGTH);  // 保存randomB到待加密缓冲区
        encryptAndSign(&enc_data, &data_size, ret_data, ret_data_len, mobile_key, car_key, uuid);   // SM2加密并签名
        printf("car data_size：%d\n", data_size);

        memcpy(send_buf, &data_size, sizeof(uint32_t));     // 将密文+签名的长度data_size保存到发送缓冲区
        memcpy(send_buf + sizeof(uint32_t), enc_data, data_size);   // 将密文和签名保存到发送缓冲区
        send(fd, send_buf, sizeof(uint32_t) + data_size, 0);        // 发送数据
        free(enc_data);
        enc_data = NULL;

        // 拿随机数C
        len = recv(fd, buf, BUF_SIZE, 0);                           // 等待移动端响应并接收数据
        memcpy(&data_size, buf, sizeof(uint32_t));                  // 获取本轮接收数据的长度
        if (verifyAndDecrypt(&dec_data, &dec_len, buf + sizeof(uint32_t), data_size, car_key, mobile_key, uuid) != SUCCESS) {
            ret = FAILURE;
            strcpy(msg + sizeof(int)*2, "Data decryption failed");
            break;
        }
        memcpy(randomC, dec_data + sizeof(uint32_t), RANDOM_LENGTH);    // 从解密后的明文中拿到randomC
        free(dec_data);
        dec_data = NULL;
        printf("randomA: ");
        printHex(randomA, RANDOM_LENGTH);
        printf("randomB: ");
        printHex(randomB, RANDOM_LENGTH);
        printf("randomC: ");
        printHex(randomC, RANDOM_LENGTH);

        // 在这里更新uid对应的时间戳时，不需要传入时间戳，由系统托管即可。因为在连接建立成功之前的数据本就应该被过滤。
        setSecretAndUpdateTime(uuid, generateSecret(randomA, randomB, randomC));
        free(randomB);

        strcpy(msg + sizeof(int)*2, "连接成功，二次连接请求结束。");
    } while(0);

    // 无论是连接成功返回的成功状态信息还是连接失败返回的错误信息，都在这里做统一的处理
    memcpy(msg, &uuid, sizeof(uint32_t));
    memcpy(msg + sizeof(uint32_t), &ret, sizeof(uint32_t));
    encryptAndSign(&enc_data, &data_size, msg,
                 sizeof(uint32_t)*2 + strlen(msg + sizeof(uint32_t)*2), mobile_key, car_key, uuid);
    memcpy(send_buf, &data_size, sizeof(uint32_t));
    memcpy(send_buf + sizeof(uint32_t), enc_data, data_size);
    send(fd, send_buf, sizeof(uint32_t) + data_size, 0);
    free(enc_data);
    enc_data = NULL;
    printf("%s\n\n\n", msg + sizeof(int)*2);

    if (ret_data != NULL) {
        free(ret_data);
        ret_data = NULL;
    }
}

/**
 * 数据处理函数 
 */
uint8_t* dataProcessing(uint8_t *data) {
    uint8_t *ret_data = (uint8_t*)malloc(128);
    strcpy(ret_data, "hello!!!!");
    return ret_data;
}

/**
 * 过滤重放数据的过滤器，在处理数据之前调用（SM4加解密 + 数字签名）
 */
void dataFilter(int fd, uint8_t *buf, size_t len, uint32_t uuid) {
    int ret = SUCCESS;
    uint32_t dec_len = 0, data_size = 0;
    uint8_t msg[128];
    uint8_t *ret_data = NULL, *dec_data = NULL;
    KEY *key = getKey(uuid);
    uint8_t *secret = key->secret;
    EC_KEY *car_key = key->eckey_car;
    EC_KEY *mobile_key = key->eckey_mobile;

    printf("正在过滤数据...\n");

    do {
        if (verifyAndDecrypt_SM4(&dec_data, &dec_len, buf, len, secret, mobile_key, uuid) != SUCCESS) {
            ret = FAILURE;
            strcpy(msg + sizeof(int)*2, "Data decryption failed");
            break;
        }

        uint32_t time;        
        memcpy(&time, dec_data + sizeof(uint32_t), sizeof(uint32_t));
        if (!requestIsValid(key, time)) {   // 根据时间戳判断是否是重放数据
            // 是重放的数据，return
            ret = FAILURE;
            strcpy(msg + sizeof(int)*2, "request has expired");
            break;
        }
        // 不是重放数据，更新请求时间戳，应对重放攻击
        updateRequestTime(key, time);
    } while(0);

    memcpy(msg, &uuid, sizeof(uint32_t));   // 将uuid保存到待加密缓冲区 
    memcpy(msg + sizeof(uint32_t), &ret, sizeof(uint32_t)); // 将响应码保存到待加密缓冲区

    if (ret == SUCCESS) {
        printf("数据有效，开始处理数据...\n");
        uint8_t *ret_data = dataProcessing(dec_data + sizeof(uint32_t) * 2);    // 调用数据处理函数处理数据
        strcpy(msg + sizeof(int)*2, ret_data);  // 将响应数据保存到待加密缓冲区
        free(ret_data);
    } else {
        printf("收到一个无效的数据\n");
    }
    if (dec_data != NULL)   free(dec_data);
    uint8_t *enc_data = NULL;
    uint32_t  enc_len = 0;

    encryptAndSign_SM4(&enc_data, &enc_len, msg, 8 + strlen(msg + 8), secret, car_key, uuid);   // SM4加密和SM2签名
    memcpy(buf, &enc_len, sizeof(uint32_t));            // 将密文+签名的长度保存到发送缓冲区
    memcpy(buf + sizeof(uint32_t), enc_data, enc_len);  // 将密文和签名保存到发送缓冲区
    send(fd, buf, sizeof(uint32_t) + enc_len, 0);       // 发送数据，完成响应
    printf("响应已完成\n");
}

int acceptReq() {
    //1.创建套接字
    int sockfd = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
    if(sockfd < 0)
    {
        perror("TCP socket failed");
        exit(1);
    }

    //2.绑定地址信息
    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_port = htons(8080);
    addr.sin_addr.s_addr = inet_addr("0.0.0.0");
    int ret = bind(sockfd,(struct sockaddr *)&addr,sizeof(addr));
    if(ret < 0)
    {
        perror("bind failed");
        exit(1);
    }

    //3.进行侦听(告诉OS内核可以与客户端建立连接了)
    //listen函数中blocklog参数，设定的是已完成三次握手并已建立连接的队列的容量
    ret = listen(sockfd,1);
    if(ret < 0)
    {
        perror("listen failed");
        exit(1);
    }

     //4.接收新连接
    struct sockaddr_in client_addr;
    socklen_t socklen = sizeof(client_addr);
    int new_sockfd = accept(sockfd,
    			(struct sockaddr *)&client_addr,&socklen);

    if(new_sockfd < 0)
    {
        perror("accept failed");
        exit(1);
    }

    return new_sockfd;
}

int main()
{
    uint8_t *buf;
    uint8_t type;
    uint32_t offset = 0, uuid, data_size;

    generatePINUnsafe();

    // 使用socket模拟蓝牙数据传输通道
    int fd = acceptReq();       // 监听并接收移动端的连接请求
    buf = (uint8_t*)malloc(sizeof(uint8_t) * BUF_SIZE);
    
    while(1) {
        int n = 0;

        // buf中数据长度不足9B则继续接收数据
        while(offset < PREFIX) {
            n = recv(fd, buf + offset, BUF_SIZE - offset, 0);
            offset += n;
        }

        /*
         * 协议格式：uid(4B) | type(1B) | data_size(4B) | data
         */
        memcpy(&uuid, buf, sizeof(uint32_t));
        memcpy(&type, buf + sizeof(uint32_t), sizeof(uint8_t));
        memcpy(&data_size, buf + sizeof(uint32_t) + sizeof(uint8_t), sizeof(uint32_t));

        // 确保本轮数据被全部接收完成，再处理数据
        while(offset < PREFIX + data_size) {
            n = recv(fd, buf + offset, BUF_SIZE - offset, 0);
            offset += n;
        }

        switch (type)
        {
        case GENERATE_PIN:
            break;
        case FIRST_CONNECT:
            firstConn(fd, buf + PREFIX, data_size, uuid);
            break;
        case SECOND_CONNECT:
            secondConn(fd, buf + PREFIX, data_size, uuid);
            break;  
        case DATA_COMMUNICATION:
            dataFilter(fd, buf + PREFIX, data_size, uuid);
            break;
        default:
            break;
        }

        offset = offset - PREFIX - data_size;
        if (offset > 0) {
            memmove(buf, buf + PREFIX + data_size, offset);
        }
    }

    free(buf);
    buf = NULL;
    close(fd);

    return 0;
}